<?php


namespace process;


use app\Enums\LogChannel;
use app\Enums\WebsocketChannel;
use app\model\DeviceCard;
use app\model\GamesRecord;
use app\model\RoomDevice;
use app\model\WebsocketErrorLogs;
use app\repository\Websocket\BaseRepository;
use app\repository\Websocket\TcpRepository;
use app\repository\Websocket\WebRepository;
use app\repository\Websocket\WebsocketRepository;
use support\Cache;
use Symfony\Component\Translation\Dumper\MoFileDumper;
use Workerman\Connection\TcpConnection;
use Workerman\Worker;

class Websocket extends BaseRepository
{
    protected $worker;
    protected $cache;

    protected $authoList = ["admin","test","develop"];

    public function onWorkerStart($worker)
    {
        $this->worker = $worker;
        $this->cache = config("websocket.cache")."_connection";
    }

    public function onConnect(TcpConnection $connection){

    }

    public function onWebSocketConnect(TcpConnection $connection, $http_buffer){

    }

    public function onMessage(TcpConnection $connection, $data)
    {
        $res = json_decode($data,true);
        dump("收到 了数据",$res);
        if (!isset($res['author']) && !isset($res['Author'])){
            $connection->send($this->result_error_data("请输入您的身份标识"));
            return ;
        }
        if ($res == 'ping'){

            $connection->send("pong");
            return;
        }


        //web 端
        if (isset($res['author'])){
            $this->check_connection_info($connection,$res['author']);
            if (!WebRepository::checkAuthor($res['author'])){
                $connection->send($this->result_error_data("您不是本系统用户"));
                return ;
            }
            if (!isset($res['function'])){
                $connection->send($this->result_error_data("请输入function"));
                return;
            }
            //不存在 rid 返回错误
            if (!isset($res['rid'])){
                $connection->send($this->result_error_data("请输入您的请求标识"));
                return;
            }

            if ($res['function'] == "init"){
                $connection->send($this->result_success_data("初始化成功",$res['rid']));
                return;
            }
            //确认开始游戏
            if ($res["function"] == "confirm"){
                $client_ip = $connection->getRemoteIp();
                $confirm_begin_game = WebsocketRepository::confirmBeginGame($client_ip);
                if (!$confirm_begin_game['status']){
                    $connection->send($this->result_error_data($confirm_begin_game['msg']));
                    return;
                }
                //$res['rid'],$res['author'],$res["to_ip"],$res['send_data']
                $this->gameReady($connection,[
                    "rid"=>$res["rid"],
                    "author"=>$res["author"],
                    "to_ip"=>$confirm_begin_game["data"]["to_ip"],
                    "send_data"=>[
                        "card_no"=>$confirm_begin_game["data"]["card_no"],
                        "time"=>120
                    ]
                ]);
                //开门
                $this->toTcpControl($confirm_begin_game["data"]["room"]['tcp_ip'],"open_door",$res["author"]);
                $this->toTcpControl($confirm_begin_game["data"]["room"]['tcp_ip'],"open_light_6",$res["author"]);
                $connection->send($this->result_success_data("",$res['rid'],$res['function']));
                return;
            }

            if ($res["function"] == "room_time_out"){
                $client_ip = $connection->getRemoteIp();
                WebsocketRepository::roomTimeOut($client_ip);
                $connection->send($this->result_success_data([],$res['rid'],$res['function']));
            }


            if ($res["function"] == "get_room_bind"){
                $client_ip = $connection->getRemoteIp();
                $room_bind = WebsocketRepository::getRoomBind($client_ip);
                $connection->send($this->result_success_data($room_bind['data'],$res['rid'],$res['function']));
            }
            if ($res["function"] == "get_go_ip"){
                $this->sendToDevice($connection,"go","get_ip",$res['rid'],$res['author']);
                return;
            }
            if ($res['function'] == 'ping'){
                $connection->send($this->result_success_data("pong",$res['rid']));
                return;
            }
            //web获取 tcp 房间列表
            if ( $res['function'] == "tcp_list"){
                $this->sendToTcpGetDeviceList($connection, $res);
                return ;
            }
            $controller_tcp_result = $this->controllerTcp($res, $connection);
            if ($controller_tcp_result){
                return;
            }

            if ($res['function'] == "send_to_game"){
                $this->sendToGame($connection,$res);
                return;
            }
            if ( $res['function'] == "ready"){
                $this->gameReady($connection,$res);
                return;
            }
            if ( $res["function"] == "begin"){
                $this->sendToDevice($connection,"game","begin",$res['rid'],$res["author"],$res["to_ip"],$res["send_data"]);
                return;
            }
            if ($res['function'] == "query_game_info"){
                $this->sendToDevice($connection,"game","query_game_info",$res['rid'],$res['author'],$res['to_ip']);
                return;
            }
            $connection->send($this->result_error_data('您的方法不存在'),$res['rid']);
            return;
        }

        //device 端
        if (isset($res['Author'])){
            $this->check_connection_info($connection,$res['Author']);
            if ($res['InputType'] == "ping")
            {
                $connection->send($this->send_data("pong","pong","1111111"));
                return ;
            }
            //初始化的时候，device 发送过来的数据
            if ($res['InputType'] == 'init')
            {
                $this->check_connection_info($connection,$res['Author']);
                //返回告诉他知道你链接了
                $connection->send($this->send_data("tcp","init",''));
                return ;
            }
            //device发送 tcp列表数据回来
            if ($res['InputType'] == "tcp_list"){
                //我发送给 author
//                if ($res['ReturnAuthor']){
                    $this->sendToWeb($res["InputData"],$res["Rid"],$res["InputType"],$res["ReturnAuthor"]);
//                    return;
//                }
                return;
            }
            if ($res['InputType'] == "tcp_control_return"){
                if ($res['Code'] == 400){
                    $sendData = $this->result_error_data($res['Message'],$res['Rid']);
                    $this->sendToWebList($sendData);
                    return;
                }
                if ($res['InputData'] && gettype($res['InputData']) == 'string'){
                    $stringArray = str_split($res['InputData']);
                    $sendData = $this->result_success_data(['data'=>$stringArray,'ip'=>$res['InputIp']],$res['Rid'],"tcp_control_return");
                    $this->sendToWebList($sendData);
                    return;
                }
                $sendData = $this->result_error_data('数据异常',$res['Rid'],"tcp_control_return");
                $this->sendToWebList($sendData);
                return;
            }
            //读卡器读取手环数据
            if ($res['InputType'] == 'udp_scan_info'){
                $res['rid'] = "112312";
                //如果 debug 开启状态，就走这个
                if (config("websocket.debug")){
                    //判断设备是哪里的
                }
                $this->handleCard($connection,$res);
                return;
            }
            if ($res['InputType'] == "game"){
                $connection_id = BaseRepository::getConnectionId($res['ReturnAuthor']);
                $to_conn = $this->getConnection($connection_id);
                $sendData = $this->result_success_data($res['InputData'],$res['Rid'],$res['InputType'],$res["Message"]);
                if (!$to_conn){
                    WebsocketRepository::websocketErrorLog(WebsocketChannel::NO_CONNECTION,$sendData,"","{$res['ReturnAuthor']}设备不在线");
                    return;
                }
                $to_conn->send($sendData);
                return;
            }
            if ($res["InputType"] == "get_ip"){
                $this->sendToWeb($res["InputData"],$res["Rid"],$res["InputType"],$res["ReturnAuthor"]);
                return;
            }

            if ($res["InputType"] == "game_ready_return"){
                $ip = $res["InputData"]["ip"];
                $room = RoomDevice::query()->where("inside_pc_ip", $ip)->first();
                $this->controllerTcp([
                    "function"=>"open_door",
                    "ip"=>$room->tcp_ip,
                    "author"=>"system",
                    "rid"=>"11112222"
                ],$connection);
                $this->sendToWeb([],$res["Rid"],"confirm",$room->author);
                return;
            }

            if ($res['InputType'] == "game_send_user_action"){
                $gameIp = $res["InputIp"];
                $returnData = $res["InputData"];
                $room = RoomDevice::query()->where("inside_pc_ip", $gameIp)->first();
                WebsocketRepository::handleUserAction($returnData["userId"]);
                //没有开始游戏直接弹出
                if ($returnData['action'] == "03"){
                    dump('003');
                    WebsocketRepository::handleUserAction($returnData["userId"]);
                    $this->toTcpControl($room->tcp_ip,"close_light_1","sys");
                    $this->toTcpControl($room->tcp_ip,"close_light_2","sys");
                    $this->toTcpControl($room->tcp_ip,"close_light_3","sys");
                    $this->toTcpControl($room->tcp_ip,"close_light_4","sys");
                    $this->toTcpControl($room->tcp_ip,"close_light_5","sys");
                    $this->toTcpControl($room->tcp_ip,"close_light_6","sys");
                }
                //开始游戏了之后又停止了
                dump("到这里了");
                if($returnData["action"] == "02"){
                    dump("0022");
                    WebsocketRepository::handleUserAction($returnData["userId"]);
                    $this->toTcpControl($room->tcp_ip,"open_light_6","sys");
                    $this->toTcpControl($room->tcp_ip,"open_door","sys");
                    $this->toTcpControl($room->tcp_ip,"close_light_1","sys");
                    $this->toTcpControl($room->tcp_ip,"close_light_2","sys");
                    $this->toTcpControl($room->tcp_ip,"close_light_3","sys");
                    $this->toTcpControl($room->tcp_ip,"close_light_4","sys");
                    $this->toTcpControl($room->tcp_ip,"close_light_5","sys");
                }
                //开始游戏
                if ($returnData["action"] == "01"){
                    $this->toTcpControl($room->tcp_ip,"close_door","sys");
                    $this->toTcpControl($room->tcp_ip,"close_light_6","sys");
                }
                $this->sendToWeb(["action" => $returnData['action']],"111","user_action",$room->author);
                return;
            }

            return;
        }
    }


    protected function toTcpControl($ip,$function,$author)
    {
        $connection_id = self::getConnectionId("device");
        $to_conn = $this->getConnection($connection_id);
        $sendData = $this->send_data("tcp",$function,"1111",$author,$ip,"control");
        if (!$to_conn){
            WebsocketRepository::websocketErrorLog(WebsocketChannel::NO_CONNECTION,$sendData,""," device该设备不在线");
            return true;
        }
        //发送数据告诉 device ，开灯
        $to_conn->send($sendData);

    }

    protected function check_connection_info(TcpConnection $connection,$author)
    {
        $consJs = Cache::get($this->cache);
        $cons = json_decode($consJs,true);
        $exsit = false;
        //判断缓存中是否有保存连接
        if ($cons){
            //校验当前的连接是否在缓存中存在
            foreach ($cons as $item){
                if ($item['id'] == $connection->id){
                    $item['author'] = $author;
                    $exsit = true;
                }
            }
            if (!$exsit && $author){
                array_push($cons,[
                    "author"=>$author,
                    "id"=>$connection->id
                ]);
            }
            //对当前连接和缓存中的数据进行校对
            $connection_id_list = $this->getConnectionIdList();
            $newCache = [];
            foreach ($cons as $con) {
                if (in_array($con['id'],$connection_id_list)){
                    $newCache[] = $con;
                }
            }
            //最后更新好数据保存
            Cache::set($this->cache,json_encode($newCache));
        }else{

            //缓存中加入刚刚的连接
            Cache::set($this->cache,json_encode([
                [
                    "author"=>$author,
                    "id"=>$connection->id
                ]
            ]));
        }

    }

    public function getConnectionIdList()
    {
        $connections = $this->worker->connections;
        $list = [];
        foreach ($connections as $connectionItem){
            $list[] = $connectionItem->id;
        }
        return $list;
    }

    /**
     * 获取 tcpconnection，没有就返回空
     * @param $id
     * @return mixed|string
     */
    protected function getConnection($id)
    {
        $connections = $this->worker->connections;
        foreach ($connections as $connectionItem){
            if ($connectionItem->id == $id){
                return $connectionItem;
            }
        }
        return '';
    }


    public function onClose(TcpConnection $connection)
    {
        $var = Cache::get($this->cache);
        if ($var){
            $connections = json_decode($var, true);
            foreach ($connections as $key => $con){
                if ($con['id'] == $connection->id){
                    WebsocketErrorLogs::create(TcpRepository::event(LogChannel::WEBSOCKET_CLOSE,["connection_id"=>$connection->id,
                        "author"=>$con['author']],"","websocket关闭了"));
                    unset($connections[$key]);
                }
            }
            Cache::set($this->cache,json_encode($connections));
        }
        // 关闭websocket
        $connection->close();
    }






    public function controllerTcp($res,$connection)
    {
        $exitsFunction = in_array($res['function'],TcpRepository::$command);
        //开关 tcp
        if (WebRepository::checkAuthor($res['author']) && $exitsFunction && isset($res['ip']))
        {
            $check_ip = TcpRepository::checkIp($res['ip']);
            if (!$check_ip){
                $connection->send($this->result_error_data("您操作的房间不存在"));
                return true;
            }
            $connection_id = BaseRepository::getConnectionId('device');
            if (!$connection_id){
                $connection->send($this->result_error_data("硬件设备不在线，请检查硬件设备系统"));
                return true;
            }
            $exit = TcpRepository::check_function($res['function']);
            if (!$exit){
                $connection->send($this->result_error_data("function 不存在",$res['rid']));
                return true;
            }
            $to_conn = $this->getConnection($connection_id);
            $sendData = $this->send_data("tcp",$res['function'],$res['rid'],$res['author'],$res['ip'],"control");
            if (!$to_conn){
                WebsocketRepository::websocketErrorLog(WebsocketChannel::NO_CONNECTION,$sendData,""," device该设备不在线");
                return true;
            }
            //发送数据告诉 device ，开灯
            $to_conn->send($sendData);
            return true;
        }
        return false;
    }

    public function sendToTcpGetDeviceList($connection,$res)
    {
        $connection_id = BaseRepository::getConnectionId('device');
        if (!$connection_id){
            $connection->send($this->result_error_data("硬件设备不在线，请检查硬件设备系统"));
            return false;
        }
        $to_conn = $this->getConnection($connection_id);
        $sendData = $this->send_data("tcp","list",$res['rid'],$res['author']);
        if (!$to_conn){
            $connection->send($this->result_error_data("硬件设备不在线，请检查硬件设备系统"));
            return false;
        }
        //发送数据告诉 device ，需要请求列表
        $to_conn->send($sendData);
        return false;
    }

    public function sendToGame($connection,$res)
    {
        $connection_id = BaseRepository::getConnectionId('device');
        if (!$connection_id){
            $connection->send($this->result_error_data("硬件设备不在线，请检查硬件设备系统"));
            return false;
        }
        $to_conn = $this->getConnection($connection_id);
        $sendData = $this->send_data("game","ready",$res['rid'],$res['author'],$res["to_ip"],$res['send_data']);
        if (!$to_conn){
            $connection->send($this->result_error_data("硬件设备不在线，请检查硬件设备系统"));
            return false;
        }
        //发送数据告诉 device ，需要请求列表
        $to_conn->send($sendData);
        $connection->send($this->result_success_data("发送数据给device了",$res['rid'],"game"));
    }

    public function gameReady($connection,$res)
    {
        $connection_id = BaseRepository::getConnectionId('device');
        if (!$connection_id){
            $connection->send($this->result_error_data("硬件设备不在线，请检查硬件设备系统"));
            return false;
        }
        $to_conn = $this->getConnection($connection_id);
        $res['send_data']["card_no"] = (string)$res["send_data"]["card_no"];
        $sendData = $this->send_data("game","ready",$res['rid'],$res['author'],$res["to_ip"],$res['send_data']);
        if (!$to_conn){
            $connection->send($this->result_error_data("硬件设备不在线，请检查硬件设备系统"));
            return false;
        }
        //发送数据告诉 device ，需要请求列表
        $to_conn->send($sendData);
        $connection->send($this->result_success_data("发送数据给device了",$res['rid'],"game"));
    }


    public function handleCard($connection,$res)
    {
        //检查是不是前台读卡器
        $handle_udp_scan = WebsocketRepository::check_is_admin_Udp_sacn($res['InputIp']);
        if ($handle_udp_scan['status']) {
            //是前台设备,直接将卡号发过去
            $connection_id = BaseRepository::getConnectionId("admin");
            $to_conn = $this->getConnection($connection_id);
            $sendData = $this->result_success_data($res['InputData'], $res['Rid'], $res['InputType']);
            if (!$to_conn) {
                WebsocketRepository::websocketErrorLog(WebsocketChannel::NO_CONNECTION, $sendData, "", "test设备不在线");
                return;
            }
            $to_conn->send($sendData);
        }elseif ($handle_udp_scan['code'] == 300){
            //是房间设备
            $hReturn = WebsocketRepository::handleRoomScanInfo($res['InputIp'], $res['InputData']);
            if ($hReturn['status'] && $hReturn['code'] == 201){
                //超级管理员，房间页面弹出信息
                $room = $hReturn['data'];
                $this->controllerTcp([
                    "function"=>"close_light_1",
                    "ip"=>$room['tcp_ip'],
                    "author"=>"system",
                    "rid"=>"11112222"
                ],$connection);
                return;
            }
            //有异常
            if (!$hReturn['status']){
                $room = $hReturn['data'];
                dump("这个是异常",$hReturn['msg']);
                $this->sendToWeb([],"12312","message",$room['author'],$hReturn['msg'],300);
                return;
            }
            $hReturnData = $hReturn['data'];
            $room = $hReturnData['room'];
            $connection_id = BaseRepository::getConnectionId($room['author']);
            $to_conn = $this->getConnection($connection_id);
            //处理成功
            if ($hReturn['status']){
                $sendData = $this->result_success_data($hReturn['data'],$res['Rid'],$res['InputType']);
                if (!$to_conn){
                    WebsocketRepository::websocketErrorLog(WebsocketChannel::NO_CONNECTION,$sendData,"","{$room['author']}设备不在线");
                    return;
                }
                $k = 1;
                foreach ($hReturn["data"]["list"] as $item){
                    $this->controllerTcp([
                        "function"=>"open_light_".$k,
                        "ip"=>$room['tcp_ip'],
                        "author"=>"system",
                        "rid"=>"11112222"
                    ],$connection);
                    if ($k>=5){
                        break;
                    }
                    $k++;
                }
                $to_conn->send($sendData);
                return;
            }else{
                $sendData = $this->result_error_data($hReturn['msg'],$res['Rid'],$res['InputType']);
                if (!$to_conn){
                    WebsocketRepository::websocketErrorLog(WebsocketChannel::NO_CONNECTION,$sendData,"","{$room['author']}设备不在线");
                    return;
                }
                $to_conn->send($sendData);
                return;
            }
        }else{
            dump("一个不在系统内的读卡器");
            //该设备不在系统内
            WebsocketRepository::websocketErrorLog(WebsocketChannel::NO_CONNECTION,$res,"","未知设备连接");
            return;
        }
    }

    public function sendToWeb($data,$rid,$function,$author,$message = "请求成功",$code = 200)
    {
        $connection_id = BaseRepository::getConnectionId($author);
        $to_conn = $this->getConnection($connection_id);
        $sendData = $this->result_success_data($data,$rid,$function,$message, $code);
        if (!$to_conn){
            WebsocketRepository::websocketErrorLog(WebsocketChannel::NO_CONNECTION,$sendData,"","$author 设备不在线");
            return;
        }
        $to_conn->send($sendData);
    }

    public function sendToDevice($connection,$client_type,$function,$rid,$return_author = "",$to_ip = "",$data = [])
    {
        $connection_id = BaseRepository::getConnectionId('device');
        if (!$connection_id){
            $connection->send($this->result_error_data("硬件设备不在线，请检查硬件设备系统"));
            return;
        }
        $to_conn = $this->getConnection($connection_id);
        $sendData = $this->send_data($client_type,$function,$rid,$return_author,$to_ip,$data);
        if (!$to_conn){
            $connection->send($this->result_error_data("硬件设备不在线，请检查硬件设备系统"));
            return;
        }
        //发送数据告诉 device ，需要请求列表
        $to_conn->send($sendData);
        $connection->send($this->result_success_data("发送数据给device了",$rid,$function));
        return;
    }

    public function gameBegin($connection, $res)
    {
        $connection_id = BaseRepository::getConnectionId('device');
        if (!$connection_id){
            $connection->send($this->result_error_data("硬件设备不在线，请检查硬件设备系统"));
            return false;
        }
        $to_conn = $this->getConnection($connection_id);
        $sendData = $this->send_data("game","begin",$res['rid'],$res['author'],$res["to_ip"],$res['send_data']);
        if (!$to_conn){
            $connection->send($this->result_error_data("硬件设备不在线，请检查硬件设备系统"));
            return false;
        }
        //发送数据告诉 device ，需要请求列表
        $to_conn->send($sendData);
        $connection->send($this->result_success_data("发送数据给device了",$res['rid'],"game"));
    }


    public function sendToWebList($sendData)
    {
        foreach ($this->authoList as $item){
            $connection_id = BaseRepository::getConnectionId($item);
            $to_conn = $this->getConnection($connection_id);
            if (!$to_conn){
                WebsocketRepository::websocketErrorLog(WebsocketChannel::NO_CONNECTION,$sendData,"","$item 设备不在线");
            }else{
                $to_conn->send($sendData);
            }
        }

    }

}